package performance;

import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

/**
 * 资源加载工具类
 * 
 * @author renhui
 *
 */
public class ResourceLoadUtil {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(ResourceLoadUtil.class);

	/**
	 * Tries to open an input stream for a resource.
	 * 
	 * @param resource
	 *            the resource name
	 * @return an input stream for the resource or <code>null</code> if the
	 *         resource was not found
	 */
	public static InputStream getResourceInputStream(String resource) {
		InputStream is = null;

		ClassLoader classLoader = Thread.currentThread()
				.getContextClassLoader();
		if (classLoader != null) {
			is = classLoader.getResourceAsStream(resource);
		}

		if (is == null) {
			classLoader = ResourceLoadUtil.class.getClassLoader();
			if (classLoader != null) {
				is = classLoader.getResourceAsStream(resource);
			}

			if (is == null) {
				is = ResourceLoadUtil.class.getResourceAsStream("/" + resource);
			}
		}

		return is;
	}

	public static InputStream getLocationInputStream(String location) {
		InputStream is = null;

		is = getResourceInputStream(location);
		if (is == null) {
			is = getFileInputStream(location);
		}

		if (is == null) {
			is = getURLInputStream(location);
		}

		return is;
	}

	/**
	 * Tries to open a file for reading.
	 * 
	 * @param filename
	 *            the file name
	 * @return an input stream for the file or <code>null</code> if the file was
	 *         not found
	 */
	public static InputStream getFileInputStream(String filename) {
		InputStream is = null;

		File file = new File(filename);
		if (file.exists() && file.isFile()) {
			try {
				is = new FileInputStream(file);
			} catch (FileNotFoundException e) {
				throw new RuntimeException(e);
			}
		}

		return is;
	}

	/**
	 * Tries to open an input stream for an URL.
	 * 
	 * @param spec
	 *            the string to parse as an URL
	 * @return an input stream for the URL or null if <code>spec</code> is not a
	 *         valid URL
	 */
	public static InputStream getURLInputStream(String spec) {
		InputStream is = null;

		try {
			URL url = new URL(spec);
			is = url.openStream();
		} catch (MalformedURLException e) {
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

		return is;
	}

	public static List<ClassLoaderResource> getClassLoaderResources(
			String resource) {
		Map<URL, ClassLoaderResource> resources = new LinkedHashMap<URL, ClassLoaderResource>();
		collectResources(resource, ResourceLoadUtil.class.getClassLoader(),
				resources);
		if (ResourceLoadUtil.class.getClassLoader() != Thread.currentThread()
				.getContextClassLoader()) {
			collectResources(resource, Thread.currentThread()
					.getContextClassLoader(), resources);
		}
		return new ArrayList<ClassLoaderResource>(resources.values());
	}

	private static void collectResources(String resourceName,
			ClassLoader classLoader, Map<URL, ClassLoaderResource> resources) {
		if (classLoader == null) {
			return;
		}
		try {
			// creating a list of parent classloaders, with the highest in the
			// hierarchy first
			LinkedList<ClassLoader> classloaders = new LinkedList<ClassLoader>();
			ClassLoader ancestorLoader = classLoader;
			while (ancestorLoader != null) {
				classloaders.addFirst(ancestorLoader);

				try {
					ancestorLoader = ancestorLoader.getParent();
				} catch (SecurityException e) {
					// if we're not allowed to get the parent, stop here.
					// resources will be listed with the first classloader that
					// we're allowed to access.
					// one case when this happens is applets.
					// FIXME revisit logging on SecurityException for applet
					ancestorLoader = null;
				}
			}

			for (ClassLoader ancestor : classloaders) {
				Enumeration<URL> urls = ancestor.getResources(resourceName);
				if (urls != null) // class loaders should never return null on
									// getResources, according to specs, but
									// we've seen cases, so we protect our code
									// here
				{
					while (urls.hasMoreElements()) {
						URL url = urls.nextElement();
						if (!resources.containsKey(url)) {
							LOGGER.logMessage(
									LogLevel.DEBUG,
									"Found resource:[{0}] at:[{1}],in classloader:[{2}]",
									resourceName, url, ancestor);
							ClassLoaderResource resource = new ClassLoaderResource(
									url, ancestor);
							resources.put(url, resource);
						}
					}
				}
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

}
